﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

/*For observable collection */
using System.Collections.ObjectModel;

/* Threading */
/* Synchronized methods */
using System.Threading;
using System.Runtime.CompilerServices;

using GasData;

namespace WPF_DataLogger
{
    public enum DecoderType
    {
        BlueTooth,
        MRF
    }

    public enum SensorFamily
    {
        MQ,
        MICS_Inv,
        MICS
    }

    public partial class PackDecoder
    {
        public PackDecoder()
        {

        }

        public PackDecoder(byte [] packet)
        {
            
        }

        public void Reset()
        {
            if (_packet != null)
            {
                for (int i = 0; i < _packet.Length; i++)
                {
                    _packet[i] = 0x00;
                }
            }
        }

        public void Clear()
        {
            _packet = null;
        }

        public int GetNodeID()
        {
            return _packet[node_id_pos];
        }

        public virtual double GetTemp()
        {
            return 0.0;      
        }

        public virtual double GetHumid()
        {
            return 0.0;        
        }

        public virtual int GetGasNum()
        {
            return 0;   
        }

        public virtual bool IsHeaderCorrect
        {
            get
            {
                return true;
            }
        }

        public virtual bool IsTailCorrect
        {
            get
            {
                return true;
            }
        }

        public virtual int HeaderLength
        {
            get
            {
                return gas_base_pos;
            }
        }

        public DecoderType Decode_Type
        {
            get
            {
                return decoder_type;
            }
        }

        public virtual ObservableCollection<GasInfo> GetGasList(List<string> gas_name_list, SensorFamily family)
        {
            return null;
        }

        public GPS_time_s GetGPSTime()
        {
            GPS_time_s ret_time = new GPS_time_s();

            ret_time.Hour = _packet[gps_time_hour];
            ret_time.Minute = _packet[gps_time_min];
            ret_time.Second = _packet[gps_time_sec];

            return ret_time;
        }

        public int GetGPSLongitude()
        {
            return TwoBytesToInt(gps_longitude);
        }

        public int GetGPSLatitude()
        {
            return TwoBytesToInt(gps_latitude);
        }

        public virtual int PackPayloadLen
        {
            get
            {
                return 0;
            }
        }

        public virtual int SensorFieldLength
        {
            get
            {
                return 0;
            }
        }

        public virtual int PacketTailLength
        {
            get
            {
                return 0;
            }
        }

        public virtual int PreambleLength
        {
            get
            {
                return 0;
            }
        }

        public virtual bool IsPreambleData(byte data)
        {
            return true;
        }

        public virtual bool IsPreambleEnd(byte data)
        {
            return true;
        }

        protected int TwoBytesToInt(int pos)
        {
            return ((int)_packet[pos]) | ((int)_packet[pos + 1]) << 8;
        }

        public void CopyPacket(byte[] packet)
        {
            if (packet != null)
            {
                _packet = null;
                _packet = new byte[packet.Length];
                for (int i = 0; i < packet.Length; i++)
                {
                    _packet[i] = packet[i];
                }
            }

        }

        protected virtual void FormatPacket()
        {

        }

        //Return the gas concentration value in ppm
        protected double GetGasValue(int sensing_value_int, string gas_name, SensorFamily sensor_family)
        {
            /* In Kohms */
            double sensor_res = 1.0;

            /* In Kohms */
            double res_ratio = 1.0;

            double curve_slope = 1.0;

            double y_intersection = 1.0;

            double R_L =1.0; // in Kohms

            double R_O = 10; // in Kohms

            double ret =1.0;

            double factor = 1.0;

            switch (gas_name)
            {
                case "CO":
                    switch(sensor_family){
                        //In CO sensing, both MQ and MICS_Inv use the MQ-7 sensor 
                        case SensorFamily.MQ:
                        case SensorFamily.MICS_Inv:
                            curve_slope = -0.699;
                            y_intersection = 1.398;
                            R_L = 80.0;
                            R_O = 4.4;
                            if (sensing_value_int == _adc_max_value)
                            {
                                sensing_value_int = _adc_max_value - 1; //To avoid divide by 0 error;
                            }
                            sensor_res = sensing_value_int * R_L /
                                (1.0 * _adc_max_value - sensing_value_int);

                            res_ratio = sensor_res / R_O;
                            factor = 1; //ppm
                        break;

                        case SensorFamily.MICS:
                            curve_slope = -0.634;
                            y_intersection = 0.053;
                            R_L = 100;
                            R_O = 1000.0;
                            sensor_res = _adc_max_value * R_L / sensing_value_int - R_L;
                            res_ratio = sensor_res / R_O;
                            factor = 1; //ppm
                        break;
                    }

                    ret = res_ratio / (System.Math.Pow(10, y_intersection));
                    ret = System.Math.Pow(ret, 1.0 / curve_slope);
                    break;

                //By default MICS
                case "NO2":
                    switch(sensor_family){
                        case SensorFamily.MQ:
                            curve_slope = -0.329;
                            y_intersection = 0.658;
                            R_L = 80.0;
                            R_O = 10.0;

                            if (sensing_value_int == _adc_max_value)
                            {
                                //To avoid divide by 0 error;
                                sensing_value_int = _adc_max_value - 1;
                            }

                            sensor_res = sensing_value_int * R_L /
                                (1.0 * _adc_max_value - sensing_value_int);

                            res_ratio = sensor_res / R_O;
                            factor = 1; //ppm
                        break;
                        
                        case SensorFamily.MICS:
                        case SensorFamily.MICS_Inv:
                            curve_slope = 1.912;
                            y_intersection = 2.699;                 
                            R_O = 2.2;
                            if (sensor_family == SensorFamily.MICS_Inv)
                            {
                                R_L = 22.0;
                                sensor_res = sensing_value_int * R_L /
                                    (_adc_max_value - sensing_value_int);
                            }
                            else
                            {
                                R_L = 10;
                                sensor_res = _adc_max_value * R_L / sensing_value_int - R_L; ;
                            }
                            res_ratio = sensor_res / R_O;
                            factor = 1; //ppm
                        break;
                    }
                    ret = res_ratio / (System.Math.Pow(10, y_intersection));
                    ret = System.Math.Pow(ret, 1.0 / curve_slope);
                    break;

                case "O3":
                    switch (sensor_family)
                    {
                        //log(res_ratio) = k log(ppm) + y_intersection
                        //
                        case SensorFamily.MQ:
                            curve_slope = -1;
                            y_intersection = 1.301;
                            R_L = 10;
                            R_O = 100.0;

                            if (sensing_value_int == _adc_max_value)
                            {
                                //To avoid divide by 0 error;
                                sensing_value_int = _adc_max_value - 1;
                            }

                            sensor_res = sensing_value_int * R_L /
                           (1.0 * _adc_max_value - sensing_value_int);
                           
                            res_ratio = sensor_res / R_O;
                            factor = 0.001; //ppb
                            ret = res_ratio / (System.Math.Pow(10, y_intersection));
                            ret = System.Math.Pow(ret, 1.0 / curve_slope);
                            break;

                        case SensorFamily.MICS:
                            //quadratic function:
                            double tmp = Math.Log10( (double)_adc_max_value * 1.0 / sensing_value_int - 1.0);
                            tmp += 1.784; //For R_L = 10K
                            tmp = Math.Pow(tmp,2);
                            tmp /= 5.54;
                            tmp = tmp + 0.56;
                            ret = Math.Pow(10.0,tmp);
                            break;
                        case SensorFamily.MICS_Inv:
                            tmp = Math.Log10((double)_adc_max_value * 1.0 / sensing_value_int - 1.0);
                            tmp = 1.784 - tmp;
                            tmp = Math.Pow(tmp, 2);
                            tmp /= 5.54;
                            tmp = tmp + 0.56;
                            ret = Math.Pow(10.0, tmp);
                            break;

                    }
                    factor = 0.001;
                    break;

                default:
                    return 0.01;
            }

            return factor * ret;
        }

        protected byte[] _packet;

        protected DecoderType decoder_type;

        protected int temp_pos;
        protected int humid_pos;
        protected int gps_time_hour;
        protected int gps_time_min;
        protected int gps_time_sec;
        protected int gps_longitude;
        protected int gps_latitude;
        protected int node_id_pos;
        protected int pack_len_pos;
        protected int gas_num_pos;
        protected int gas_base_pos;
        protected int preamble_len;
        protected int pack_type_pos;
        protected int _adc_max_value;
        protected int[] preamble_pattern;
        protected int[] tail_pattern;
    }

     public struct GPS_time_s {
        uint hour;
        uint minute;
        uint second;

        public uint Hour
        {
            get
            {
                return hour;
            }

            set
            {
                hour = value;
            }
        }

         public uint Minute
         {
                 get
                {
                    return minute;
                }

                set
                {
                    minute = value;
                }
         }

         public uint Second
         {
                 get
                {
                    return second;
                }

                set
                {
                    second = value;
                }
         }
    }

    public delegate void PacketFormat();
    public delegate ObservableCollection<GasInfo> CreateGasInfoCollection(List<string> gas_name_list);
}
